/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package mamayukero.problem.environment.component;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import mamayukero.debug.LogContainer;
import mamayukero.logic.pathfinding.*;
import mamayukero.problem.environment.Environment;

public class Truck {
    public Point location;
    public ArrayList<Point> goal;
    public boolean firstWalk; //,isMoving
    public ArrayList<Machine> machines;
    public int capacity,    //kapasitas maksimal truk
            direction,      //arah jalan (0) atas, (1) kanan, (2) bawah, (3) kiri
            find;
    public String pickupSchedule;
    
    public LogContainer log;
    
    public static final int DIR_UP = 0
                            ,DIR_RIGHT = 1
                            ,DIR_DOWN = 2
                            ,DIR_LEFT = 3;
    
    //pathfinding
    public int pfstate;
    public ArrayList<PathFindingBase> pathfinding;
    
    public static final int PF_ASTAR = 0,
                            PF_BFS = 1,
                            PF_GREEDY = 2;
    
    public Truck() {
        goal = new ArrayList<Point>();
        location = new Point(0,0);
        machines = new ArrayList<Machine>();
        //isMoving = false;
        firstWalk = true;
        direction = DIR_RIGHT;
        pfstate = PF_ASTAR;
        find = 0;
        pathfinding = new ArrayList<PathFindingBase>();
        pickupSchedule = "0000";
        log = new LogContainer("TRUCK");
    }
    
    public final void initMap() {
        location = new Point(Environment.map.factory.getLocation());
        pathfinding.add(new AStar(Environment.map));
        pathfinding.add(new BFS(Environment.map));
        pathfinding.add(new Greedy(Environment.map));
    }
    
    public void move() {
        boolean more = true;
        if (goal.isEmpty()) {
            if (isFactory())
                give();
            else
                take(find);
            more = work();
        }
        if (more) {
            location = goal.get(0);
            goal.remove(0);
        }
    }
    
    public List<Point> findPath(Point p) {
        ArrayList<Point> result = new ArrayList<Point>();
        switch (pfstate) {
            case PF_ASTAR:
                result = (((AStar) pathfinding.get(0)).solve(location,p)).point;
                break;
            case PF_BFS:
                result = (((BFS) pathfinding.get(1)).solve(location,p)).point;
                break;
            case PF_GREEDY:
                result = (((Greedy) pathfinding.get(2)).solve(location,p)).point;
                break; 
        }
        return result.subList(1, result.size());
    }
    
    public void give() {
        //give all machine to factory
        while (!machines.isEmpty()) {
            log.append("Give machine " + (Integer.toString(machines.get(0).type+1)) + " to the factory");
            Environment.map.factory.machines.add(machines.get(0));
            machines.remove(0);
        }
    }
    
    public boolean demand(int d) {
        char c = ((char) (d+48));
        for (int k = 0; k < pickupSchedule.length(); k++) {
            if (pickupSchedule.charAt(k) == c)
                return true;
        }
        return false;
    }
    
    public int take(int machineType) {
        boolean found = false;
        int result = -1;
        int i = 0, j = 0;
        while ((!found) && (i < Environment.map.vendor.size())) {
            found = (Environment.map.vendor.get(i).getLocation().equals(location));
            if (found) {
                while ((machines.size() < capacity) && (result < 0) && (j < Environment.map.vendor.get(i).machines.size())) {
                    result = Environment.map.vendor.get(i).own(machineType);
                    if (result >= 0) {
                            Machine n = Environment.map.vendor.get(i).machines.get(result);
                            log.append("Take machine " + Integer.toString(n.type+1) + " from vendor " + (Integer.toString(i+1)));
                            machines.add(n);
                            Environment.map.vendor.get(i).machines.remove(result);
                            if (firstWalk) {
                                int k = 0;
                                while ((k < Environment.map.vendor.get(i).machines.size()) && (machines.size() < capacity)) {
                                    Machine m = Environment.map.vendor.get(i).machines.get(k);
                                    if ((machines.size() < capacity) && (demand(m.type))) {
                                        machines.add(m);
                                        Environment.map.vendor.get(i).machines.remove(k);
                                        removeFromSchedule((char) (48 + m.type));
                                        log.append("Take machine " + Integer.toString(m.type+1) + " from vendor " + (Integer.toString(i+1)));
                                    } else 
                                        k++;
                                }
                            }
                    }
                    j++;
                }
            }
            i++;
        }
        return result;
    }
    
    public int isVendor() {
        for (Vendor v: Environment.map.vendor) {
            if (v.getLocation() == location) return v.ID;
        }
        return -1;
    }
    
    public boolean isFactory() {
        return ((location.x == Environment.map.factory.getLocation().x) && (location.y == Environment.map.factory.getLocation().y));
    }
    
    public boolean finish() {                       //have i completed my work?
        return ((isFactory()) &&                    //is in the factory
                (goal.isEmpty()) &&                 //has no goal
                (pickupSchedule.isEmpty()));        //end of schedule
    }
    
    public void removeFromSchedule(Character c) {
        StringBuilder scheduleBuilder = new StringBuilder();
        for (int i = 0; i < pickupSchedule.length(); i++) {
            if (pickupSchedule.charAt(i) != c) scheduleBuilder.append(pickupSchedule.charAt(i));
        }
        pickupSchedule = scheduleBuilder.toString();
    }
    
    public boolean work() {
        if ((!isFactory()) && ((firstWalk) || (pickupSchedule.isEmpty()))) {
            ArrayList<Point> pathToFactory = new ArrayList<Point>(findPath(Environment.map.factory.getLocation()));
            goal = new ArrayList<Point>(pathToFactory);
            //isMoving = true;
            firstWalk = false;
        } else if (!pickupSchedule.isEmpty()) {
            Character c = pickupSchedule.charAt(0);
            find = c - 48;
            if (findClosestVendor(find)) removeFromSchedule(c);
            //isMoving = true;
        }
        return !goal.isEmpty();
    }
    
    public boolean findClosestVendor(int m) {
        boolean result = false;
        ArrayList<Point> pathToFactory = (isFactory()? new ArrayList<Point>() : new ArrayList<Point>(findPath(Environment.map.factory.getLocation())));
        ArrayList<Point> pathToVendor;
        int minLength = (isFactory()? -1 : pathToFactory.size());
        int machineFound;
        if (machines.size() < capacity) {
            for (Vendor v: Environment.map.vendor) {
                machineFound = v.own(m);
                if (machineFound >= 0) {
                    pathToVendor = new ArrayList<Point>(findPath(v.getLocation()));
                    if ((minLength == -1) || (pathToVendor.size() <= minLength)) {
                        goal = new ArrayList<Point>(pathToVendor);
                        minLength = pathToVendor.size();
                        result = true;
                    }
                }
            }
        }
        if ((!isFactory()) && (!result)) {
            goal = new ArrayList<Point>(pathToFactory);
        }
        return result;
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("pickup schedule = [" + pickupSchedule + "]\n" + "[T] (" + location.x + "," + location.y + ") ");
        for (Machine m : machines) {
            sb.append("[").append(m.type).append("] ");
        }
        sb.append("\n");
        for (Point p : goal) {
            sb.append("[").append(p.x).append(",").append(p.y).append("]");
        }
        return sb.toString();
    } 
}
